#include "EpilStringObject.h"
#include "EpilInterpreter.h"
#include "EpilInterpreterException.h"
#include "EpilObjectHelper.h"


EpilStringObject::EpilStringObject( EpilInterpreter * interpreter, const tstring & value )
: EpilValueObject( interpreter )
, m_value( value )
{
}


EpilStringObject::~EpilStringObject()
{
}


EpilObject * EpilStringObject::OperateUnary( EpilUnaryOperator op )
{
	switch( op )
	{
	case EUO_Opposite:
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a string value.") );
	case EUO_Length:
		return this->GetInterpreter()->CreateNumberObject( static_cast<int>( this->GetValue().length() ) );
	case EUO_Not:
		return this->GetInterpreter()->CreateBooleanObject( false );
	}
	throw EpilInterpreterException( t("Invalid unary operator detected.") );
}


EpilObject * EpilStringObject::OperateBinary( EpilBinaryOperator op, EpilObject * that )
{
	switch( that->GetType() )
	{
	case EOT_Nil:
		{
			EpilNilObject * thatNil = static_cast<EpilNilObject *>( that );
			return this->OperateBinaryInternal( op, thatNil );
		}
	case EOT_Boolean:
		{
			EpilBooleanObject * thatBoolean = static_cast<EpilBooleanObject *>( that );
			return this->OperateBinaryInternal( op, thatBoolean );
		}
	case EOT_Number:
		{
			EpilNumberObject * thatDouble = static_cast<EpilNumberObject *>( that );
			return this->OperateBinaryInternal( op, thatDouble );
		}
	case EOT_String:
		{
			EpilStringObject * thatStr = static_cast<EpilStringObject *>( that );
			return this->OperateBinaryInternal( op, thatStr );
		}
	}
	return NULL;
}


EpilObject * EpilStringObject::OperateBinaryInternal( EpilBinaryOperator op, EpilNilObject * that )
{
	switch( op )
	{
	case EBO_And:
		return this->GetInterpreter()->CreateNilObject();
	case EBO_Or:
		return this->CreateCopy();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare string with nil.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a string value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a nil value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilStringObject::OperateBinaryInternal( EpilBinaryOperator op, EpilBooleanObject * that )
{
	switch( op )
	{
	case EBO_And:
		return that->CreateCopy();
	case EBO_Or:
		return this->CreateCopy();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare string with boolean.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a string value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a boolean value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilStringObject::OperateBinaryInternal( EpilBinaryOperator op, EpilNumberObject * that )
{
	switch( op )
	{
	case EBO_And:
		return that->CreateCopy();
	case EBO_Or:
		return this->CreateCopy();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare string with number.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a string value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		return this->GetInterpreter()->CreateStringObject( this->GetValue() + EpilObjectHelper::ToString( that->GetValue() ) );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilStringObject::OperateBinaryInternal( EpilBinaryOperator op, EpilStringObject * that )
{
	switch( op )
	{
	case EBO_And:
		return that->CreateCopy();
	case EBO_Or:
		return this->CreateCopy();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() == that->GetValue() );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() != that->GetValue() );
	case EBO_LessThan:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() < that->GetValue() );
	case EBO_LessThanOrEqual:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() <= that->GetValue() );
	case EBO_GreaterThan:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() > that->GetValue() );
	case EBO_GreaterThanOrEqual:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() >= that->GetValue() );
	default:
		break;
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a string value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		return this->GetInterpreter()->CreateStringObject( this->GetValue() + that->GetValue() );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


bool EpilStringObject::operator==( const tstring & rhs ) const
{
	return m_value == rhs;
}


bool EpilStringObject::operator==( const EpilObject & rhs ) const
{
	if( rhs.GetType() != EOT_String )
	{
		return false;
	}
	const EpilStringObject & obj = static_cast<const EpilStringObject &>( rhs );
	return *this == obj;
}


bool EpilStringObject::operator==( const EpilStringObject & rhs ) const
{
	return m_value == rhs.GetValue();
}


EpilValueObject * EpilStringObject::Clone() const
{
	EpilStringObject * ret = this->GetInterpreter()->CreateStringObject( m_value );
	return ret;
}


tstring EpilStringObject::ToString() const
{
	return m_value;
}